package com.david.nfc

import android.app.Activity
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.nfc.*
import android.nfc.tech.Ndef
import android.os.Build
import android.os.Parcelable
import android.util.Log
import java.io.IOException
import java.io.UnsupportedEncodingException
import java.nio.charset.Charset
import kotlin.experimental.and


object NfcUtils {
    const val ERROR_DETECTED = "No NFC tag detected!"
    const val WRITE_SUCCESS = "Text written to the NFC tag successfully!"
    const val WRITE_ERROR = "Error during writing, is the NFC tag close enough to your device?"

    var nfcAdapter: NfcAdapter? = null
    var myTag: Tag? = null
    private var writeMode = false
    private var writeTagFilters: Array<IntentFilter>

    init {
        val tagDetected = IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED)
        tagDetected.addCategory(Intent.CATEGORY_DEFAULT)
        writeTagFilters = arrayOf(tagDetected)
    }

    fun init(context: Context){
        nfcAdapter = NfcAdapter.getDefaultAdapter(context)
    }

    /******************************************************************************
     * Read From NFC Tag
     ****************************************************************************/
    fun readFromIntent(intent: Intent):String {
        val action = intent.action
        if (NfcAdapter.ACTION_TAG_DISCOVERED == action || NfcAdapter.ACTION_TECH_DISCOVERED == action || NfcAdapter.ACTION_NDEF_DISCOVERED == action) {
            myTag = if (Build.VERSION.SDK_INT >= 33) {
                intent.getParcelableExtra(NfcAdapter.EXTRA_TAG, Tag::class.java)
            } else {
                intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
            }
            val rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
            val msgs = mutableListOf<NdefMessage>()
            if (rawMsgs != null) {
                for (i in rawMsgs.indices) {
                    msgs.add(i, rawMsgs[i] as NdefMessage)
                }
                return buildTagViews(msgs.toTypedArray())
            }
        }
        return ""
    }

    private fun buildTagViews(msgs: Array<NdefMessage>): String {
        if (msgs.isEmpty()) return ""
        var text = ""
        val payload = msgs[0].records[0].payload
        val textEncoding: Charset = if ((payload[0] and 128.toByte()).toInt() == 0) Charsets.UTF_8 else Charsets.UTF_16 // Get the Text Encoding
        val languageCodeLength: Int = (payload[0] and 51).toInt() // Get the Language Code, e.g. "en"
        try {
            // Get the Text
            text = String(
                payload,
                languageCodeLength + 1,
                payload.size - languageCodeLength - 1,
                textEncoding
            )
        } catch (e: UnsupportedEncodingException) {
            Log.e("UnsupportedEncoding", e.toString())
        }
        return text
    }

    /******************************************************************************
     * Write to NFC Tag
     ****************************************************************************/
    @Throws(IOException::class, FormatException::class)
    fun write(text: String, tag: Tag?) {
        val records = arrayOf(createRecord(text))
        val message = NdefMessage(records)
        // Get an instance of Ndef for the tag.
        val ndef = Ndef.get(tag)
        // Enable I/O
        ndef.connect()
        // Write the message
        ndef.writeNdefMessage(message)
        // Close the connection
        ndef.close()
    }

    /******************************************************************************
     * Enable Write and foreground dispatch to prevent intent-filter to launch the app again
     ****************************************************************************/
    fun writeModeOn(activity: Activity, pendingIntent: PendingIntent) {
        writeMode = true
        nfcAdapter!!.enableForegroundDispatch(activity, pendingIntent, writeTagFilters, null)
    }

    /******************************************************************************
     * Disable Write and foreground dispatch to allow intent-filter to launch the app
     ****************************************************************************/
    fun writeModeOff(activity: Activity) {
        writeMode = false
        nfcAdapter!!.disableForegroundDispatch(activity)
    }

    @Throws(UnsupportedEncodingException::class)
    fun createRecord(text: String): NdefRecord {
        val lang = "en"
        val textBytes = text.toByteArray()
        val langBytes = lang.toByteArray(charset("US-ASCII"))
        val langLength = langBytes.size
        val textLength = textBytes.size
        val payload = ByteArray(1 + langLength + textLength)

        // set status byte (see NDEF spec for actual bits)
        payload[0] = langLength.toByte()

        // copy langbytes and textbytes into payload
        System.arraycopy(langBytes, 0, payload, 1, langLength)
        System.arraycopy(textBytes, 0, payload, 1 + langLength, textLength)
        return NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, ByteArray(0), payload)
    }

    fun hasNfc(context: Context?): Boolean {
        var bRet = false
        if (context == null) return bRet
        val manager = context.getSystemService(Context.NFC_SERVICE) as NfcManager
        val adapter = manager.defaultAdapter
        if (adapter != null && adapter.isEnabled) {
            // adapter存在，能启用
            bRet = true
        }
        return bRet
    }

    fun release(){
        nfcAdapter = null
    }
}